home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / python / debpython / pydist.py < prev    next >
Encoding:
Python Source  |  2012-05-04  |  7.5 KB  |  213 lines

  1. # -*- coding: UTF-8 -*-
  2. # Copyright ┬⌐ 2010 Piotr O┼╝arowski <piotr@debian.org>
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a copy
  5. # of this software and associated documentation files (the "Software"), to deal
  6. # in the Software without restriction, including without limitation the rights
  7. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. # copies of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be included in
  12. # all copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. # THE SOFTWARE.
  21.  
  22. from __future__ import with_statement
  23. import logging
  24. import os
  25. import re
  26. from os.path import exists, isdir, join
  27. from subprocess import PIPE, Popen
  28. from debpython.version import vrepr, getver, get_requested_versions
  29. from debpython.tools import memoize
  30.  
  31. log = logging.getLogger(__name__)
  32.  
  33. PUBLIC_DIR_RE = re.compile(r'.*?/usr/lib/python(\d.\d+)/(site|dist)-packages')
  34. PYDIST_RE = re.compile(r"""
  35.     (?P<name>[A-Za-z][A-Za-z0-9_.]*)             # Python distribution name
  36.     \s*
  37.     (?P<vrange>(?:-?\d\.\d+(?:-(?:\d\.\d+)?)?)?) # version range
  38.     \s*
  39.     (?P<dependency>(?:[a-z][^;]*)?)              # Debian dependency
  40.     (?:  # optional upstream version -> Debian version translator
  41.         ;\s*
  42.         (?P<standard>PEP386)?                    # PEP-386 mode
  43.         \s*
  44.         (?P<rules>s/.*)?                         # translator rules
  45.     )?
  46.     """, re.VERBOSE)
  47. REQUIRES_RE = re.compile(r'''
  48.     (?P<name>[A-Za-z][A-Za-z0-9_.]*)     # Python distribution name
  49.     \s*
  50.     (?P<enabled_extras>(?:\[[^\]]*\])?)  # ignored for now
  51.     \s*
  52.     (?:  # optional minimum/maximum version
  53.         (?P<operator><=?|>=?|==|!=)
  54.         \s*
  55.         (?P<version>(\w|[-.])+)
  56.     )?
  57.     ''', re.VERBOSE)
  58.  
  59.  
  60. def validate(fpath, exit_on_error=False):
  61.     """Check if pydist file looks good."""
  62.     with open(fpath) as fp:
  63.         for line in fp:
  64.             line = line.strip('\r\n')
  65.             if line.startswith('#') or not line:
  66.                 continue
  67.             if not PYDIST_RE.match(line):
  68.                 log.error('invalid pydist data in file %s: %s', \
  69.                           fpath.rsplit('/', 1)[-1], line)
  70.                 if exit_on_error:
  71.                     exit(3)
  72.                 return False
  73.     return True
  74.  
  75.  
  76. @memoize
  77. def load(dname='/usr/share/python/dist/', fname='debian/pydist-overrides',
  78.          fbname='/usr/share/python/dist_fallback'):
  79.     """Load iformation about installed Python distributions."""
  80.     if exists(fname):
  81.         to_check = [fname]  # first one!
  82.     else:
  83.         to_check = []
  84.     if isdir(dname):
  85.         to_check.extend(join(dname, i) for i in os.listdir(dname))
  86.     if exists(fbname):  # fall back generated at python-defaults build time
  87.         to_check.append(fbname)  # last one!
  88.  
  89.     result = {}
  90.     for fpath in to_check:
  91.         with open(fpath) as fp:
  92.             for line in fp:
  93.                 line = line.strip('\r\n')
  94.                 if line.startswith('#') or not line:
  95.                     continue
  96.                 dist = PYDIST_RE.search(line)
  97.                 if not dist:
  98.                     log.error('%s file has a broken line: %s', fpath, line)
  99.                     exit(9)
  100.                 dist = dist.groupdict()
  101.                 name = safe_name(dist['name'])
  102.                 dist['versions'] = get_requested_versions(dist['vrange'])
  103.                 dist['dependency'] = dist['dependency'].strip()
  104.                 if dist['rules']:
  105.                     dist['rules'] = dist['rules'].split(';')
  106.                 else:
  107.                     dist['rules'] = []
  108.                 result.setdefault(name, []).append(dist)
  109.     return result
  110.  
  111.  
  112. def guess_dependency(req, version=None):
  113.     log.debug('trying to guess dependency for %s (python=%s)',
  114.               req, vrepr(version) if version else None)
  115.     if isinstance(version, basestring):
  116.         version = getver(version)
  117.  
  118.     # some upstreams have weird ideas for distribution name...
  119.     name, rest = re.compile('([^><= ]+)(.*)').match(req).groups()
  120.     req = safe_name(name) + rest
  121.  
  122.     data = load()
  123.     req_dict = REQUIRES_RE.match(req)
  124.     if not req_dict:
  125.         log.error('requirement is not valid: %s', req)
  126.         log.info('please ask dh_python2 author to fix REQUIRES_RE '
  127.                  'or your upstream author to fix requires.txt')
  128.         exit(8)
  129.     req_dict = req_dict.groupdict()
  130.     details = data.get(req_dict['name'].lower())
  131.     if details:
  132.         for item in details:
  133.             if version and version not in item.get('versions', version):
  134.                 # rule doesn't match version, try next one
  135.                 continue
  136.  
  137.             if not item['dependency']:
  138.                 return  # this requirement should be ignored
  139.             if item['dependency'].endswith(')'):
  140.                 # no need to translate versions if version is hardcoded in Debian
  141.                 # dependency
  142.                 return item['dependency']
  143.             if req_dict['version']:
  144.                 # FIXME: translate it (rules, versions)
  145.                 return item['dependency']
  146.             else:
  147.                 return item['dependency']
  148.  
  149.     # try dpkg -S
  150.     query = "'%s-?*\.egg-info'" % safe_name(name)  # TODO: .dist-info
  151.     if version:
  152.         query = "%s | grep '/python%s/\|/pyshared/'" % \
  153.                 (query, vrepr(version))
  154.     else:
  155.         query = "%s | grep '/python2\../\|/pyshared/'" % query
  156.  
  157.     log.debug("invoking dpkg -S %s", query)
  158.     process = Popen("/usr/bin/dpkg -S %s" % query, \
  159.                     shell=True, stdout=PIPE)
  160.     stdout, stderr = process.communicate()
  161.     if process.returncode == 0:
  162.         result = set()
  163.         for line in stdout.split('\n'):
  164.             if not line.strip():
  165.                 continue
  166.             result.add(line.split(':')[0])
  167.         if len(result) > 1:
  168.             log.error('more than one package name found for %s dist', name)
  169.         else:
  170.             return result.pop()
  171.  
  172.     # fall back to python-distname
  173.     pname = sensible_pname(name)
  174.     log.warn('Cannot find package that provides %s. '
  175.              'Using %s as package name. Please add "%s correct_package_name" '
  176.              'line to debian/pydist-overrides to override it.',
  177.              name, pname, name)
  178.     return pname
  179.  
  180.  
  181. def parse_pydep(fname):
  182.     public_dir = PUBLIC_DIR_RE.match(fname)
  183.     if public_dir:
  184.         ver = public_dir.group(1)
  185.     else:
  186.         ver = None
  187.  
  188.     result = []
  189.     with open(fname, 'r') as fp:
  190.         for line in fp:
  191.             line = line.strip()
  192.             # ignore all optional sections
  193.             if line.startswith('['):
  194.                 break
  195.             if line:
  196.                 dependency = guess_dependency(line, ver)
  197.                 if dependency:
  198.                     result.append(dependency)
  199.     return result
  200.  
  201.  
  202. def safe_name(name):
  203.     """Emulate distribute's safe_name."""
  204.     return re.compile('[^A-Za-z0-9.]+').sub('_', name).lower()
  205.  
  206.  
  207. def sensible_pname(egg_name):
  208.     """Guess Debian package name from Egg name."""
  209.     egg_name = safe_name(egg_name).replace('_', '-')
  210.     if egg_name.startswith('python-'):
  211.         egg_name = egg_name[7:]
  212.     return "python-%s" % egg_name.lower()
  213.